package com.kulian.Signalling.distribution;


import com.alibaba.fastjson.JSON;
import com.kulian.Signalling.common.result.Result;


import com.kulian.Signalling.core.util.ServletUtils;
import com.kulian.Signalling.distribution.mode.*;
import com.kulian.Signalling.distribution.mode.Package;
import com.kulian.Signalling.distribution.service.AppService;
import com.kulian.Signalling.distribution.service.FileService;
import com.kulian.Signalling.distribution.service.PackageServer;
import com.kulian.Signalling.distribution.service.PackageService;
import com.kulian.Signalling.distribution.utils.FileDownloadUtil;
import com.kulian.Signalling.distribution.utils.PathManager;
import com.kulian.Signalling.distribution.utils.QRCodeUtil;
import com.kulian.Signalling.distribution.utils.ipa.PlistGenerator;
import com.kulian.Signalling.distribution.utils.webhook.WebHookClient;
import com.kulian.Signalling.distribution.vo.AppPageVO;
import com.kulian.Signalling.distribution.vo.AppViewModel;
import com.kulian.Signalling.distribution.vo.PackageVO;
import com.kulian.Signalling.distribution.vo.PackageViewModel;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.*;
import jakarta.persistence.PostRemove;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter;

import java.io.*;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;

@Tag(name = "07.文件接口")
@RestController
@RequestMapping("/api/v1/distribution/package")
@RequiredArgsConstructor
public class PackageController {

    // 建议的缓冲区大小 (8KB-64KB之间平衡性能与内存使用)
    private static final int BUFFER_SIZE = 90 * 1024 + 1024; // 8KB

    private final FileService fileService;

    @Resource
    private AppService appService;
    @Resource
    private PackageService packageService;
    @Resource
    private PathManager pathManager;
    @Resource
    private PackageServer packageServer;
    @Value("${oss.local.storage-path}")
    private String storagePath;

    ServletOutputStream os = null;
    FileInputStream fis = null;
    BufferedInputStream bis = null;

    /**
     * 预览页
     * @param code
     * @param request
     * @return
     */
    @GetMapping("/s/{code}")
    public String get(@PathVariable("code") String code, HttpServletRequest request) {
        String id = request.getParameter("id");
        AppViewModel viewModel = this.appService.findByCode(code, id);
        request.setAttribute("app", viewModel);
        request.setAttribute("ca_path", this.pathManager.getCAPath());
        request.setAttribute("basePath", this.pathManager.getBaseURL(false));
        return "install";
    }

    /**
     * 设备列表
     * @param id
     * @param request
     * @return
     */
    @GetMapping("/devices/{id}")
    public String devices(@PathVariable("id") String id, HttpServletRequest request) {
        PackageViewModel viewModel= this.packageService.findById(id);
        request.setAttribute("app", viewModel);
        return "devices";
    }

    /**
     * 安装教程
     * @param platform
     * @param request
     * @return
     */
    @GetMapping("/guide/{platform}")
    public String guide(@PathVariable("platform") String platform, HttpServletRequest request) {
        request.setAttribute("platform", platform);
        return "guide";
    }

    /**
     * 上传包
     * @param file
     * @param request
     * @return
     */
    @PostMapping(value = "/upload")
    @Operation(summary = "文件上传")
    public Map<String, Object> upload(@RequestParam("file") MultipartFile file, HttpServletRequest request) {
        Map<String, Object> map = new HashMap<>();
        try {

//            String Path = Global.getUploadPath();
            String appId = request.getParameter("appId");
            String packageId = request.getParameter("packageId");
            String version = request.getParameter("version");
            String uid = request.getParameter("uid");
            Map packageInfo = new HashMap();
            packageInfo.put("appId",appId);
            packageInfo.put("packageId",packageId);
            packageInfo.put("version",version);

            // 上传并返回新文件名称
//            String fileName = FileUploadUtils.upload(file,packageInfo);
//            String url = getUrl() + fileName;

            String filePath = transfer(file);
            Package aPackage = this.packageService.buildPackage(filePath);
            Map<String , String> extra = new HashMap<>();
            String jobName = request.getParameter("jobName");
            String buildNumber = request.getParameter("buildNumber");
            if (StringUtils.hasLength(jobName)) {
                extra.put("jobName", jobName);
            }
            if (StringUtils.hasLength(buildNumber)) {
                extra.put("buildNumber", buildNumber);
            }
            if (!extra.isEmpty()) {
                aPackage.setExtra(JSON.toJSONString(extra));
            }
            App app = this.appService.getByPackage(aPackage);
            app.getPackageList().add(aPackage);
            app.setCurrentPackage(aPackage);
            app.setUid(uid);
            aPackage.setApp(app);
            app = this.appService.saves(app);
            // URL
            String codeURL = this.pathManager.getBaseURL(false) + "/p/code/" + app.getCurrentPackage().getId();
            // 发送WebHook消息
            WebHookClient.sendMessage(app, pathManager);
            map.put("code", codeURL);
            map.put("success", true);
        } catch (Exception e) {
            map.put("success", false);
            e.printStackTrace();
        }
        return map;
    }

//    @PostMapping(value = "/upload")
//    @Operation(summary = "文件上传")
//    public Result<FileInfo> uploadFile(
//            @Parameter(
//                    name = "file",
//                    description = "表单文件对象",
//                    required = true,
//                    in = ParameterIn.DEFAULT,
//                    schema = @Schema(name = "file", format = "binary")
//            )
//            @RequestPart(value = "file") MultipartFile file
//    ) {
//        FileInfo fileInfo = fileService.uploadFile(file);
//        return Result.success(fileInfo);
//    }

    @DeleteMapping
    @Operation(summary = "文件删除")
    @SneakyThrows
    public Result<?> deleteFile(
            @Parameter(description = "文件路径") @RequestParam String filePath
    ) {
        boolean result = fileService.deleteFile(filePath);
        return Result.judge(result);
    }
    /**
     * 下载文件源文件(ipa 或 apk)
     * @param id
     * @param response
     */
    @RequestMapping("/p/{id}")
    public void download(@PathVariable("id") String id, HttpServletResponse response) {
        try {


            Package aPackage = this.packageService.get(id);
            String path = PathManager.getFullPath(aPackage) + aPackage.getFileName();
            File file = new File(path);
            if(file.exists()){ //判断文件父目录是否存在
                response.setContentType("application/force-download");
                // 文件名称转换
                String fileName = aPackage.getName() + "_" + aPackage.getVersion();
                String ext =  "." + FilenameUtils.getExtension(aPackage.getFileName());
                String appName = new String(fileName.getBytes("UTF-8"), "iso-8859-1");
                response.setHeader("Content-Disposition", "attachment;fileName=" + appName + ext);

                byte[] buffer = new byte[1024];
                OutputStream os = response.getOutputStream();
                FileInputStream fis = new FileInputStream(file);
                BufferedInputStream bis = new BufferedInputStream(fis);
                int i = bis.read(buffer);
                while(i != -1){
                    os.write(buffer);
                    i = bis.read(buffer);
                }
                bis.close();
                fis.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @GetMapping("/icon/{id}")
    public void getPrice(@PathVariable("id") String id, HttpServletResponse response){
        PackageVO aPackage = this.packageServer.getPackageInfo(id);
        String filePrefix = storagePath.endsWith(File.separator) ? storagePath : storagePath + File.separator;
//        String path = PathManager.getFullPath(aPackage) + aPackage.getFileName();
        File file = new File(filePrefix + aPackage.getFiles() + File.separator + aPackage.getBundleid() + File.separator + aPackage.getVersion() + File.separator  + aPackage.getIconId() + ".png");
       try {
           if(file.exists()){ //判断文件父目录是否存在
               response.setContentType("application/force-download");
               // 文件名称转换
               String fileName = aPackage.getName() + "_" + aPackage.getVersion() + ".png";
               String ext =  "." + FilenameUtils.getExtension(aPackage.getFileName());
               String appName = new String(fileName.getBytes("UTF-8"), "iso-8859-1");
               response.setHeader("Content-Disposition", "attachment;fileName=" + appName);

               byte[] buffer = new byte[1024];
               OutputStream os = response.getOutputStream();
               FileInputStream fis = new FileInputStream(file);
               BufferedInputStream bis = new BufferedInputStream(fis);
               int i = bis.read(buffer);
               while(i != -1){
                   os.write(buffer);
                   i = bis.read(buffer);
               }
               bis.close();
               fis.close();
           }
       }catch (Exception e) {
           e.printStackTrace();
       }


    }
    @Operation(summary = "下载页")
    @GetMapping("/download")
    public Result getInsert(@Parameter(description = "pid", example = "") @RequestParam String pid){
        PackageVO aPackage = this.packageServer.getPackageInfo(pid);
        Map<String, Object> map = new HashMap<>();
        map.put("data",aPackage);
        return Result.success(aPackage);
    }

    @GetMapping("/insert")
    public void getApp(@Parameter(description = "pid", example = "") @RequestParam String pid,HttpServletResponse response){
        PackageVO aPackage = this.packageServer.getPackageInfo(pid);
        String filePrefix = storagePath.endsWith(File.separator) ? storagePath : storagePath + File.separator;
//        String path = PathManager.getFullPath(aPackage) + aPackage.getFileName();
        File file = new File(filePrefix + aPackage.getFiles() + File.separator + aPackage.getBundleid() + File.separator + aPackage.getVersion() + File.separator  + aPackage.getFileName());
        if(aPackage.getPlatform() == "ios"){
            try {

                if (aPackage != null) {
                    response.setContentType("application/force-download");
                    response.setHeader("Content-Disposition", "attachment;fileName=manifest.plist");
                    Writer writer = new OutputStreamWriter(response.getOutputStream());
                    PlistGenerator.generateios(aPackage, writer);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }else{
            FileDownloadUtil.safeDownload(response,file,false,aPackage);
        }


//        try {
//            if(file.exists()){ //判断文件父目录是否存在
//                response.setContentType("application/force-download");
//                // 文件名称转换
//                String fileName = aPackage.getFileName();
//                String ext =  "." + FilenameUtils.getExtension(aPackage.getFileName());
//                String appName = new String(fileName.getBytes("UTF-8"), "iso-8859-1");
//                response.setContentType("application/vnd.android.package-archive");
//                response.setHeader("Content-Disposition", "attachment;fileName=" + appName);
//
//                os = response.getOutputStream();
//                fis = new FileInputStream(file);
//                bis = new BufferedInputStream(fis);
//
//                byte[] buffer = new byte[BUFFER_SIZE];
//                int bytesRead;
//
//                while ((bytesRead = bis.read(buffer)) != -1) {
//                    try {
//                        os.write(buffer, 0, bytesRead);
//                    } catch (IOException e) {
//                        if (e.getMessage().contains("Broken pipe")) {
////                            log.debug("客户端中断下载: " + file.getName());
//                            return;
//                        }
//                        throw e;
//                    }
//                }
//
//                // 最后刷新一次，但不强制
//                try {
//                    os.flush();
//                } catch (IOException e) {
//                    // 忽略最后的flush失败
//                    e.printStackTrace();
//                }
//            }
//        }catch (Exception e) {
//            if (!e.getMessage().contains("Broken pipe")) {
////                log.error("下载失败", e);
//                e.printStackTrace();
//                throw new RuntimeException("下载失败", e);
//            }
//        }finally {
//            // 单独关闭每个流，确保资源释放
//            try {
//                if (bis != null) bis.close();
//            } catch (IOException e) {
//                e.printStackTrace();
////                log.debug("关闭BufferedInputStream时出错", e);
//            }
//            try {
//                if (fis != null) fis.close();
//            } catch (IOException e) {
//                e.printStackTrace();
////                log.debug("关闭FileInputStream时出错", e);
//            }
//            try {
//                if (os != null) os.close();
//            } catch (IOException e) {
//                e.printStackTrace();
////                log.debug("关闭OutputStream时出错", e);
//            }
//        }


    }

    /**
     * 获取 manifest
     * @param id
     * @param response
     */
    @GetMapping("/m")
    public void getManifest( @RequestParam String id, HttpServletResponse response) {
        try {
            PackageViewModel viewModel = this.packageService.findById(id);
            if (viewModel != null && viewModel.isiOS()) {
                response.setContentType("application/force-download");
                response.setHeader("Content-Disposition", "attachment;fileName=manifest.plist");
                Writer writer = new OutputStreamWriter(response.getOutputStream());
                PlistGenerator.generate(viewModel, writer);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取包二维码
     * @param id
     * @param response
     */
    @RequestMapping("/p/code/{id}")
    public void getQrCode(@PathVariable("id") String id, HttpServletResponse response) {
        try {
            PackageViewModel viewModel = this.packageService.findById(id);
            if (viewModel != null) {
                response.setContentType("image/png");
                QRCodeUtil.encode(viewModel.getPreviewURL()).withSize(250, 250).writeTo(response.getOutputStream());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 删除包
     * @param id
     * @return
     */
    @RequestMapping("/p/delete/{id}")
    @ResponseBody
    public Map<String, Object> deleteById(@PathVariable("id") String id) {
        Map<String, Object> map = new HashMap<>();
        try {
            this.packageService.deleteById(id);
            map.put("success", true);
        } catch (Exception e) {
            map.put("success", false);
        }
        return map;
    }

    /**
     * 转存文件
     * @param srcFile
     * @return
     */
    private String transfer(MultipartFile srcFile) {
        try {
            // 获取文件后缀
            String fileName = srcFile.getOriginalFilename();
            String ext = FilenameUtils.getExtension(fileName);
            // 生成文件名
            String newFileName = UUID.randomUUID().toString() + "." + ext;
            // 转存到 tmp
            String destPath = FileUtils.getTempDirectoryPath() + File.separator + newFileName;
            destPath = destPath.replaceAll("//", "/");
            srcFile.transferTo(new File(destPath));
            return destPath;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public File convertMultipartFileToFile(MultipartFile multipartFile) {
        // 获取上传文件的原始文件名
        String fileName = multipartFile.getOriginalFilename();
        // 创建一个新的File对象，指定文件的保存位置和文件名
        File file = new File("你的保存路径/" + fileName);

        // 确保保存路径存在
        file.getParentFile().mkdirs();

        try (FileOutputStream fos = new FileOutputStream(file);
             InputStream is = multipartFile.getInputStream()) {

            // 创建一个缓冲区
            byte[] buffer = new byte[1024];
            int len;

            // 从MultipartFile中读取内容并写入到File中
            while ((len = is.read(buffer)) != -1) {
                fos.write(buffer, 0, len);
            }

            // 关闭流，并返回File对象
            return file;

        } catch (IOException e) {
            e.printStackTrace();
            // 根据实际情况处理异常，例如抛出自定义异常或返回null
            return null;
        }
    }
    public String getUrl()
    {
        HttpServletRequest request = ServletUtils.getRequest();
        return getDomain(request);
    }
    public static String getDomain(HttpServletRequest request)
    {
        StringBuffer url = request.getRequestURL();
        String contextPath = request.getServletContext().getContextPath();
        return url.delete(url.length() - request.getRequestURI().length(), url.length()).append(contextPath).toString();
    }

}
